﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using HttpServer.Headers;
using HttpServer.Resources;
using HttpServer.Messages;

namespace HttpServer.Modules
{
    /// <summary>
    /// Serves files in the web server.
    /// </summary>
    /// <example>
    /// <code>
    /// FileModule fileModule = new FileModule();
    /// fileModule.Resources.Add(new FileResources("/", "C:\\inetpub\\myweb"));
    /// </code>
    /// </example>
    public class VirtualFileModule : IModule
    {
        private readonly Dictionary<string, ContentTypeHeader> _contentTypes =
            new Dictionary<string, ContentTypeHeader>();

        private readonly IResourceProvider _resourceManager;

        /// <summary>
        /// Initializes a new instance of the <see cref="FileModule"/> class.
        /// </summary>
        /// <exception cref="ArgumentNullException"><c>baseUri</c> or <c>basePath</c> is <c>null</c>.</exception>
        public VirtualFileModule()
        {
            _resourceManager = new ResourceProvider();
            AddDefaultMimeTypes();
            
        }

        /// <summary>
        /// Gets a list with all allowed content types. 
        /// </summary>
        /// <remarks>All other mime types will result in <see cref="HttpStatusCode.Forbidden"/>.</remarks>
        public IDictionary<string, ContentTypeHeader> ContentTypes
        {
            get { return _contentTypes; }
        }

        /// <summary>
        /// Gets provider used to add files to the file manager,
        /// </summary>
        public IResourceProvider Resources
        {
            get { return _resourceManager; }
        }

        /// <summary>
        /// Mime types that this class can handle per default
        /// </summary>
        /// <remarks>

        public void AddDefaultMimeTypes()
        {
            ContentTypes.Add("default", new ContentTypeHeader("application/octet-stream"));
            ContentTypes.Add("", new ContentTypeHeader("text/html"));
        }


        /// <summary>
        /// Will send a file to client.
        /// </summary>
        /// <param name="context">HTTP context containing outbound stream.</param>
        /// <param name="response">Response containing headers.</param>
        /// <param name="stream">File stream</param>
        private void SendFile(IHttpContext context, IResponse response, Stream stream)
        {
            response.ContentLength.Value = stream.Length;

            var generator = HttpFactory.Current.Get<ResponseWriter>();
            generator.SendHeaders(context, response);
            generator.SendBody(context, stream);
        }

        #region IModule Members

        /// <summary>
        /// Process a request.
        /// </summary>
        /// <param name="context">Request information</param>
        /// <returns>What to do next.</returns>
        /// <exception cref="InternalServerException">Failed to find file extension</exception>
        /// <exception cref="ForbiddenException">Forbidden file type.</exception>
        public ProcessingResult Process(RequestContext context)
        {
            Resource resource = _resourceManager.Get(context.Request.Uri.LocalPath);
            if (resource == null)
                return ProcessingResult.Continue;

            IRequest request = context.Request;
            IResponse response = context.Response;
            try
            {
                string fileExtension = Path.GetExtension(request.Uri.AbsolutePath).TrimStart('.');

                ContentTypeHeader header = new ContentTypeHeader("application/octet-stream");
                if (string.IsNullOrEmpty(fileExtension))
                    header = new ContentTypeHeader("text/html");

                response.ContentType = header;

               using (resource.Stream)
                {

                    //response.Add(new DateHeader("Last-Modified", resource.ModifiedAt));

                    // Send response and tell server to do nothing more with the request.
                    SendFile(context.HttpContext, response, resource.Stream);
                    return ProcessingResult.Abort;
                }
            }
            catch (FileNotFoundException err)
            {
                throw new InternalServerException("Failed to process file '" + request.Uri + "'.", err);
            }
        }

        #endregion
    }
}